package com.summli.basic.class06;

/**
 * 给定两个可能有环也可能无环的单链表，头节点head1和head2。
 * 请实现一个函数，如果两个链表相交，请返回相交的 第一个节点。如果不相交，返回null
 * 【要求】
 * 如果两个链表长度之和为N，时间复杂度请达到O(N)，额外空间复杂度 请达到O(1)。
 */
public class Code05_FindFirstIntersectNode {

    public static class Node {
        public int value;
        public Node next;

        public Node(int data) {
            this.value = data;
        }
    }
    public static Node getIntersectNode(Node head1, Node head2) {
        if(head1 == null || head2 == null){
            return null;
        }
        Node loopNode1 = getLoopNode(head1);
        Node loopNode2 = getLoopNode(head2);
        //如果是两个无环单向链表的相交问题
        if(loopNode1 == null && loopNode2 == null){
            return noLoop(head1,head2);
        }
        //两个有环相交的问题
        if(loopNode1 != null && loopNode2 != null){
            return bothLoop(head1,loopNode1,head2,loopNode2);
        }
        //一个无环和有环的相交的问题--这种一定不存在相交情况
        return null;
    }

    // 两个有环的相交的问题
    private static Node bothLoop(Node head1, Node loopNode1,Node head2, Node loopNode2) {
        //第一种相交在入环节点之前，第二种，相交在环点之内，返回任意一个就行，第三种不相交
        if(loopNode1 == loopNode2){
            int count = 0;
            Node cur1 = head1;
            Node cur2 = head2;
            while(cur1 != loopNode1){
                count++;
                cur1 = cur1.next;
            }
            while(cur2 != loopNode2){
                count--;
                cur2 = cur2.next;
            }
            cur1 = count>0? head1:head2;
            cur2 = cur1==head1? head2:head1;
            count = Math.abs(count);
            while(count != 0){
                cur1 = cur1.next;
                count--;
            }
            while(cur1 != cur2){
                cur1 = cur1.next;
                cur2 = cur2.next;
            }
            return cur1;
        }else{
            Node cur = loopNode1.next;
            while(cur != loopNode1){
                if(cur == loopNode2){
                    return loopNode1;
                }
                cur = cur.next;
            }
            return null;
        }
    }

    // 两个无环的相交问题
    private static Node noLoop(Node head1, Node head2) {
        int num = 0;//记录差值
        Node cur1 = head1;
        while(cur1 != null){
            num++;
            cur1 = cur1.next;
        }
        Node cur2 = head2;
        while(cur2 != null){
            num--;
            cur2 = cur2.next;
        }
        cur1 = num >0? head1:head2;
        cur2 = cur1 == head1?head2:head1;
        num = Math.abs(num);
        while(num != 0){
            num--;
            cur1 = cur1.next;
        }
        while(cur1 != cur2){
            cur1 = cur1.next;
            cur2 = cur2.next;
        }
        return cur1;
    }

    /*
    返回链表的入环的第一个节点
    如果一个单向链表有环，那么只需要使用快慢指针，当两者相遇的时候，将快指针从头开始，布长为1，下次两者相遇的时候，就是第一个入环节点
     */
    private static Node getLoopNode(Node head) {
        if(head == null || head.next == null || head.next.next == null){
            return null;
        }
        Node slowNode = head.next;
        Node fastNode = head.next.next;
        while(slowNode != fastNode){
            if(fastNode.next == null || fastNode.next.next == null){
                return null;//无环
            }
            slowNode = slowNode.next;
            fastNode = fastNode.next.next;
        }
        fastNode = head;
        while(slowNode != fastNode){
            slowNode = slowNode.next;
            fastNode = fastNode.next;
        }
        return fastNode;
    }

    public static void main(String[] args) {
        System.out.println("============无环链表的相交问题=================");
        // 1->2->3->4->5->6->7->null
        Node head1 = new Node(1);
        head1.next = new Node(2);
        head1.next.next = new Node(3);
        head1.next.next.next = new Node(4);
        head1.next.next.next.next = new Node(5);
        head1.next.next.next.next.next = new Node(6);
        head1.next.next.next.next.next.next = new Node(7);

        // 0->9->8->6->7->null
        Node head2 = new Node(0);
        head2.next = new Node(9);
        head2.next.next = new Node(8);
        head2.next.next.next = head1.next.next.next.next.next; // 8->6
        System.out.println(getIntersectNode(head1, head2).value);


        System.out.println("============两个有环链表的相交问题（交点在环外）=================");
        // 1->2->3->4->5->6->7->4...
        head1 = new Node(1);
        head1.next = new Node(2);
        head1.next.next = new Node(3);
        head1.next.next.next = new Node(4);
        head1.next.next.next.next = new Node(5);
        head1.next.next.next.next.next = new Node(6);
        head1.next.next.next.next.next.next = new Node(7);
        head1.next.next.next.next.next.next = head1.next.next.next; // 7->4

        // 0->9->8->2...
        head2 = new Node(0);
        head2.next = new Node(9);
        head2.next.next = new Node(8);
        head2.next.next.next = head1.next; // 8->2
        System.out.println(getIntersectNode(head1, head2).value);

        System.out.println("============两个有环链表的相交问题（交点在环内）=================");
        // 0->9->8->6->4->5->6..
        head2 = new Node(0);
        head2.next = new Node(9);
        head2.next.next = new Node(8);
        head2.next.next.next = head1.next.next.next.next.next; // 8->6
        System.out.println(getIntersectNode(head1, head2).value);

    }
}
